﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Drawing.Text;

namespace VirusCleaner.Controls
{

    public class ExaminePanel : Control
    {
        #region Fields

        private Image _image;
        private string _stateText;
        private string _scoreText;

       
        private const RoundStyle _roundStyle = RoundStyle.All;
        private int _radius = 8;
        private Color _baseColor = Color.FromArgb(191, 233, 255);
        private Color _borderColor = Color.FromArgb(118, 208, 225);
        private Color _progressBarTrackColor = Color.Gainsboro;
        private Color _progressBarBarColor = Color.FromArgb(191, 233, 255);
        private Color _progressBarBorderColor = Color.FromArgb(118, 208, 225);
        private Color _progressBarTextColor = Color.FromArgb(0, 95, 147);
        private ControlState _rescanState;

        internal ControlState RescanState
        {
            get { return _rescanState; }
            set { _rescanState = value; }
        }
        private int _value;
        [DefaultValue(0)]
        public int Value
        {
            get { return _value; }
            set
            {
                if (value > _maxiMum)
                    return;
                _value = value;
                base.Invalidate(ProgressBarRect);
                base.Invalidate(TransfersSizeRect);
                Invalidate();
            }
        }
        private int _maxiMum;
        [DefaultValue(100)]
        public int Maximum
        {
            get { return _maxiMum; }
            set
            {
                _maxiMum = value;
                base.Invalidate();
            }
        }
        private ControlState _cancelState;

        private static readonly object EventReScanButtonClick = new object();
        private static readonly object EventCancelButtonClick = new object();

        #endregion

        #region Constructors

        public ExaminePanel()
            : base()
        {
            SetStyles();
        }

        #endregion

        #region Events
        public event EventHandler ReScanButtonClick
        {
            add { base.Events.AddHandler(EventReScanButtonClick, value); }
            remove { base.Events.RemoveHandler(EventReScanButtonClick, value); }
        }

        public event EventHandler CancelButtonClick
        {
            add { base.Events.AddHandler(EventCancelButtonClick, value); }
            remove { base.Events.RemoveHandler(EventCancelButtonClick, value); }
        }

        #endregion

        #region Properties

        [DefaultValue(typeof(Image), "null")]
        public Image Image
        {
            get { return _image; }
            set
            {
                _image = value;
                base.Invalidate();
            }
        }
        [DefaultValue(typeof(String), "this's state text.")]
        public string ScoreText
        {
            get { return _scoreText; }
            set { _scoreText = value;
            base.Invalidate();
            }
        }
        [DefaultValue(typeof(String), "this's state text.")]
        public string StateText
        {
            get { return _stateText; }
            set { _stateText = value;
            base.Invalidate();
            }
        }
        [DefaultValue(8)]
        public int Radius
        {
            get { return _radius; }
            set
            {
                if (_radius != value)
                {
                    _radius = value < 4 ? 4 : value;
                    base.Invalidate();
                }
            }
        }

        [DefaultValue(typeof(Color), "191, 233, 255")]
        public Color BaseColor
        {
            get { return _baseColor; }
            set
            {
                _baseColor = value;
                base.Invalidate();
            }
        }

        [DefaultValue(typeof(Color), "118, 208, 225")]
        public Color BorderColor
        {
            get { return _borderColor; }
            set
            {
                _borderColor = value;
                base.Invalidate();
            }
        }

        [DefaultValue(typeof(Color), "Gainsboro")]
        public Color ProgressBarTrackColor
        {
            get { return _progressBarTrackColor; }
            set
            {
                _progressBarTrackColor = value;
                base.Invalidate(ProgressBarRect);
            }
        }

        [DefaultValue(typeof(Color), "191, 233, 255")]
        public Color ProgressBarBarColor
        {
            get { return _progressBarBarColor; }
            set
            {
                _progressBarBarColor = value;
                base.Invalidate(ProgressBarRect);
            }
        }

        [DefaultValue(typeof(Color), "118, 208, 225")]
        public Color ProgressBarBorderColor
        {
            get { return _progressBarBorderColor; }
            set
            {
                _progressBarBorderColor = value;
                base.Invalidate(ProgressBarRect);
            }
        }

        [DefaultValue(typeof(Color), "0, 95, 147")]
        public Color ProgressBarTextColor
        {
            get { return _progressBarTextColor; }
            set
            {
                _progressBarTextColor = value;
                base.Invalidate(ProgressBarRect);
            }
        }        

        internal Rectangle ImageRect
        {
            get { return new Rectangle(20, 10, 75, 75); }
        }

        internal Rectangle TextRect
        {
            get { return new Rectangle(100, 15, Width - 45, 30); }
        }
        internal Rectangle ScoreTextRect
        {
            get { return new Rectangle(Width - 200, 10, Width - 45, 30); }
        }
        internal Rectangle ProgressBarRect
        {
            get { return new Rectangle(100, 40, Width - 200, 16); }
        }

        internal Rectangle StateRect
        {
            get { return new Rectangle(100, 59, Width / 2 - 2, 16); }
        }

        internal Rectangle TransfersSizeRect
        {
            get { return new Rectangle(Width / 2, 59, Width / 2 - 5, 16); }
        }

        internal Rectangle CancelScanButtonRect
        {
            get
            {
                Size size = TextRenderer.MeasureText(
                    CancelButtonText,
                    _fontState);
                return new Rectangle(
                    Width - size.Width - 40,
                    40,
                    size.Width,
                    size.Height);
            }
        }
        internal Rectangle ReScanButtonRect
        {
            get
            {
                Size size = TextRenderer.MeasureText(
                    ReScanButtonText,
                    _fontState);
                return new Rectangle(
                    Width - size.Width - 100,
                    59,
                    size.Width,
                    size.Height);
            }
        }

        protected override Size DefaultSize
        {
            get { return new Size(200, 95); }
        }

        private ControlState CancelState
        {
            get { return _cancelState; }
            set
            {
                if (_cancelState != value)
                {
                    _cancelState = value;
                    base.Invalidate(Inflate(CancelScanButtonRect));
                }
            }
        }

        #endregion

        #region Virtual Methods


        protected virtual void OnReScanButtonClick(EventArgs e)
        {
            this.Invalidate();
            EventHandler handler =
                base.Events[EventReScanButtonClick] as EventHandler;
            if (handler != null)
            {
                handler(this, e);
            }
           
        }

        protected virtual void OnCancelButtonClick(EventArgs e)
        {
            this.Invalidate();
            EventHandler handler =
                base.Events[EventCancelButtonClick] as EventHandler;
            if (handler != null)
            {
                handler(this, e);
            }
            
        }

        #endregion

        #region Override Methods

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            Point point = e.Location;

            CancelState = CancelScanButtonRect.Contains(point) ? ControlState.Hover : ControlState.Normal;
            RescanState = ReScanButtonRect.Contains(point) ? ControlState.Hover : ControlState.Normal;
            Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Button != MouseButtons.Left)
            {
                return;
            }
            Point point = e.Location;

            if (CancelScanButtonRect.Contains(point))
            {
                CancelState = ControlState.Pressed;
            }
            if (ReScanButtonRect.Contains(point))
            {
                RescanState = ControlState.Pressed;
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button != MouseButtons.Left)
            {
                return;
            }
            Point point = e.Location;

            if (CancelScanButtonRect.Contains(point))
            {
                CancelState = ControlState.Hover;
                OnCancelButtonClick(e);
            }
            else
            {
                CancelState = ControlState.Normal;
            }
            if (ReScanButtonRect.Contains(point))
            {
                RescanState = ControlState.Hover;
                OnReScanButtonClick(e);
            }
            else
            {
                RescanState = ControlState.Normal;
            }

        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            CancelState = ControlState.Normal;
            RescanState = ControlState.Normal;
        }

        private readonly Font _fontState = new Font("微软雅黑", 9, FontStyle.Regular);
        private readonly Font _fontScore = new Font("微软雅黑", 18, FontStyle.Bold);


        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.InterpolationMode = InterpolationMode.HighQualityBilinear;
            g.TextRenderingHint = TextRenderingHint.AntiAlias;

            if (Image != null)
            {
                g.DrawImage(
                    Image,
                    ImageRect);
            }

            const TextFormatFlags flags = TextFormatFlags.Left |
                                          TextFormatFlags.Top |
                                          TextFormatFlags.SingleLine |
                                          TextFormatFlags.EndEllipsis;

            TextRenderer.DrawText(
                g,
                Text,
                Font,
                TextRect,
                ForeColor,
                flags);
            TextRenderer.DrawText(
                g,
                ScoreText,
                _fontScore,
                ScoreTextRect,
                ScoreForeColor,
                flags);
            TextRenderer.DrawText(
                   g,
                   _stateText,
                   _fontState,
                   StateRect,
                   ForeColor,
                   TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
            Rectangle rect = ProgressBarRect;
            Color innerBorderColor = Color.FromArgb(200, 255, 255, 255);
            if (_value >= 0)
            {
                RenderBackgroundInternal(
                    g,
                    rect,
                    _progressBarTrackColor,
                    _progressBarBorderColor,
                    innerBorderColor,
                    RoundStyle.All,
                    8,
                    .45f,
                    true,
                    false,
                    LinearGradientMode.Vertical);

                if (_maxiMum != 0)
                {
                    float percent = (float)_value / _maxiMum;
                    int width = (int)(rect.Width * percent);
                    width = Math.Min(width, rect.Width - 2);
                    if (width > 5)
                    {
                        Rectangle barRect = new Rectangle(
                            rect.X + 1,
                            rect.Y + 1,
                            width,
                            rect.Height - 2);
                        RenderBackgroundInternal(
                           g,
                           barRect,
                           _progressBarBarColor,
                           _progressBarBarColor,
                           innerBorderColor,
                           RoundStyle.All,
                           8,
                           .45F,
                           true,
                           false,
                           LinearGradientMode.Vertical);
                    }                    
                }            
            }
            if (CancelState != ControlState.Normal)
            {
                rect = CancelScanButtonRect;
                rect.Inflate(2, 2);
                RenderBackgroundInternal(
                    g,
                    rect,
                    _baseColor,
                    _borderColor,
                    innerBorderColor,
                    RoundStyle.All,
                    _radius,
                    0.45f,
                    true,
                    true,
                    LinearGradientMode.Vertical);
            } 
            if (RescanState != ControlState.Normal)
            {
                rect = ReScanButtonRect;
                rect.Inflate(2, 2);
                RenderBackgroundInternal(
                    g,
                    rect,
                    _baseColor,
                    _borderColor,
                    innerBorderColor,
                    RoundStyle.All,
                    _radius,
                    0.45f,
                    true,
                    true,
                    LinearGradientMode.Vertical);
            }

            TextRenderer.DrawText(
                g,
                CancelButtonText,
                _fontState,
                CancelScanButtonRect,
                ForeColor,
                flags);
            TextRenderer.DrawText(
                g,
                ReScanButtonText,
                _fontState,
                ReScanButtonRect,
                ForeColor,
                flags);
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            RenderBackgroundInternal(
                g,
                ClientRectangle,
                _baseColor,
                _borderColor,
                Color.FromArgb(200, 255, 255),
                _roundStyle,
                _radius,
                .45F,
                true,
                false,
                LinearGradientMode.Vertical);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                //_fileTransfersText = null;
            }
        }

        #endregion

        #region Help Methods

        private void SetStyles()
        {
            SetStyle(
                ControlStyles.UserPaint |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.FixedHeight, true);
            SetStyle(ControlStyles.Opaque, false);
            UpdateStyles();
        }

        internal void RenderBackgroundInternal(
          Graphics g,
          Rectangle rect,
          Color baseColor,
          Color borderColor,
          Color innerBorderColor,
          RoundStyle style,
          int roundWidth,
          float basePosition,
          bool drawBorder,
          bool drawGlass,
          LinearGradientMode mode)
        {
            if (drawBorder)
            {
                rect.Width--;
                rect.Height--;
            }

            if (rect.Width <= 0 || rect.Height <= 0)
            {
                return;
            }

            using (var brush = new LinearGradientBrush(
                rect, Color.Transparent, Color.Transparent, mode))
            {
                var colors = new Color[4];
                colors[0] = GetColor(baseColor, 0, 35, 24, 9);
                colors[1] = GetColor(baseColor, 0, 13, 8, 3);
                colors[2] = baseColor;
                colors[3] = GetColor(baseColor, 0, 68, 69, 54);

                var blend = new ColorBlend
                                {
                                    Positions = new float[] {0.0f, basePosition, basePosition + 0.05f, 1.0f},
                                    Colors = colors
                                };
                brush.InterpolationColors = blend;
                if (style != RoundStyle.None)
                {
                    using (GraphicsPath path =
                        GraphicsPathHelper.CreatePath(rect, roundWidth, style, false))
                    {
                        g.FillPath(brush, path);
                    }

                    if (baseColor.A > 80)
                    {
                        Rectangle rectTop = rect;

                        if (mode == LinearGradientMode.Vertical)
                        {
                            rectTop.Height = (int)(rectTop.Height * basePosition);
                        }
                        else
                        {
                            rectTop.Width = (int)(rect.Width * basePosition);
                        }
                        using (GraphicsPath pathTop = GraphicsPathHelper.CreatePath(
                            rectTop, roundWidth, RoundStyle.Top, false))
                        {
                            using (var brushAlpha =
                                new SolidBrush(Color.FromArgb(80, 255, 255, 255)))
                            {
                                g.FillPath(brushAlpha, pathTop);
                            }
                        }
                    }

                    if (drawGlass)
                    {
                        RectangleF glassRect = rect;
                        if (mode == LinearGradientMode.Vertical)
                        {
                            glassRect.Y = rect.Y + rect.Height * basePosition;
                            glassRect.Height = (rect.Height - rect.Height * basePosition) * 2;
                        }
                        else
                        {
                            glassRect.X = rect.X + rect.Width * basePosition;
                            glassRect.Width = (rect.Width - rect.Width * basePosition) * 2;
                        }
                        DrawGlass(g, glassRect, 170, 0);
                    }

                    if (drawBorder)
                    {
                        using (GraphicsPath path =
                            GraphicsPathHelper.CreatePath(rect, roundWidth, style, false))
                        {
                            using (Pen pen = new Pen(borderColor))
                            {
                                g.DrawPath(pen, path);
                            }
                        }

                        rect.Inflate(-1, -1);
                        using (GraphicsPath path =
                            GraphicsPathHelper.CreatePath(rect, roundWidth, style, false))
                        {
                            using (Pen pen = new Pen(innerBorderColor))
                            {
                                g.DrawPath(pen, path);
                            }
                        }
                    }
                }
                else
                {
                    g.FillRectangle(brush, rect);
                    if (baseColor.A > 80)
                    {
                        Rectangle rectTop = rect;
                        if (mode == LinearGradientMode.Vertical)
                        {
                            rectTop.Height = (int)(rectTop.Height * basePosition);
                        }
                        else
                        {
                            rectTop.Width = (int)(rect.Width * basePosition);
                        }
                        using (var brushAlpha =
                            new SolidBrush(Color.FromArgb(80, 255, 255, 255)))
                        {
                            g.FillRectangle(brushAlpha, rectTop);
                        }
                    }

                    if (drawGlass)
                    {
                        RectangleF glassRect = rect;
                        if (mode == LinearGradientMode.Vertical)
                        {
                            glassRect.Y = rect.Y + rect.Height * basePosition;
                            glassRect.Height = (rect.Height - rect.Height * basePosition) * 2;
                        }
                        else
                        {
                            glassRect.X = rect.X + rect.Width * basePosition;
                            glassRect.Width = (rect.Width - rect.Width * basePosition) * 2;
                        }
                        DrawGlass(g, glassRect, 200, 0);
                    }

                    if (drawBorder)
                    {
                        using (Pen pen = new Pen(borderColor))
                        {
                            g.DrawRectangle(pen, rect);
                        }

                        rect.Inflate(-1, -1);
                        using (Pen pen = new Pen(innerBorderColor))
                        {
                            g.DrawRectangle(pen, rect);
                        }
                    }
                }
            }
        }

        private void DrawGlass(
            Graphics g, RectangleF glassRect, int alphaCenter, int alphaSurround)
        {
            DrawGlass(g, glassRect, Color.White, alphaCenter, alphaSurround);
        }

        private void DrawGlass(
            Graphics g,
            RectangleF glassRect,
            Color glassColor,
            int alphaCenter,
            int alphaSurround)
        {
            using (var path = new GraphicsPath())
            {
                path.AddEllipse(glassRect);
                using (var brush = new PathGradientBrush(path))
                {
                    brush.CenterColor = Color.FromArgb(alphaCenter, glassColor);
                    brush.SurroundColors = new Color[] { 
                        Color.FromArgb(alphaSurround, glassColor) };
                    brush.CenterPoint = new PointF(
                        glassRect.X + glassRect.Width / 2,
                        glassRect.Y + glassRect.Height / 2);
                    g.FillPath(brush, path);
                }
            }
        }

        private Color GetColor(Color colorBase, int a, int r, int g, int b)
        {
            int a0 = colorBase.A;
            int r0 = colorBase.R;
            int g0 = colorBase.G;
            int b0 = colorBase.B;

            if (a + a0 > 255) { a = 255; } else { a = Math.Max(a + a0, 0); }
            if (r + r0 > 255) { r = 255; } else { r = Math.Max(r + r0, 0); }
            if (g + g0 > 255) { g = 255; } else { g = Math.Max(g + g0, 0); }
            if (b + b0 > 255) { b = 255; } else { b = Math.Max(b + b0, 0); }

            return Color.FromArgb(a, r, g, b);
        }

       
        private Rectangle Inflate(Rectangle rect)
        {
            rect.Inflate(2, 2);
            return rect;
        }

       

        internal enum ControlState
        {
            /// <summary>
            ///  正常。
            /// </summary>
            Normal,
            /// <summary>
            /// 鼠标进入。
            /// </summary>
            Hover,
            /// <summary>
            /// 鼠标按下。
            /// </summary>
            Pressed,
            /// <summary>
            /// 获得焦点。
            /// </summary>
            Focused,
        }
        public enum RoundStyle
        {
            /// <summary>
            /// 四个角都不是圆角。
            /// </summary>
            None = 0,
            /// <summary>
            /// 四个角都为圆角。
            /// </summary>
            All = 1,
            /// <summary>
            /// 左边两个角为圆角。
            /// </summary>
            Left = 2,
            /// <summary>
            /// 右边两个角为圆角。
            /// </summary>
            Right = 3,
            /// <summary>
            /// 上边两个角为圆角。
            /// </summary>
            Top = 4,
            /// <summary>
            /// 下边两个角为圆角。
            /// </summary>
            Bottom = 5
        }


        public string CancelButtonText { get; set; }

        public string ReScanButtonText { get; set; }

        public Color ScoreForeColor { get; set; }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // ExaminePanel
            // 
            Invalidate();
            this.ResumeLayout(false);

        }
    }
    public static class GraphicsPathHelper
    {

        /// <summary>
        /// 建立带有圆角样式的路径。
        /// </summary>
        /// <param name="rect">用来建立路径的矩形。</param>
        /// <param name="_radius">圆角的大小。</param>
        /// <param name="style">圆角的样式。</param>
        /// <param name="correction">是否把矩形长宽减 1,以便画出边框。</param>
        /// <returns>建立的路径。</returns>
        public static GraphicsPath CreatePath(
            Rectangle rect, int radius, ExaminePanel.RoundStyle style, bool correction)
        {
            GraphicsPath path = new GraphicsPath();
            int radiusCorrection = correction ? 1 : 0;
            switch (style)
            {
                case ExaminePanel.RoundStyle.None:
                    path.AddRectangle(rect);
                    break;
                case ExaminePanel.RoundStyle.All:
                    path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
                    path.AddArc(
                        rect.Right - radius - radiusCorrection,
                        rect.Y,
                        radius,
                        radius,
                        270,
                        90);
                    path.AddArc(
                        rect.Right - radius - radiusCorrection,
                        rect.Bottom - radius - radiusCorrection,
                        radius,
                        radius, 0, 90);
                    path.AddArc(
                        rect.X,
                        rect.Bottom - radius - radiusCorrection,
                        radius,
                        radius,
                        90,
                        90);
                    break;
                case ExaminePanel.RoundStyle.Left:
                    path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
                    path.AddLine(
                        rect.Right - radiusCorrection, rect.Y,
                        rect.Right - radiusCorrection, rect.Bottom - radiusCorrection);
                    path.AddArc(
                        rect.X,
                        rect.Bottom - radius - radiusCorrection,
                        radius,
                        radius,
                        90,
                        90);
                    break;
                case ExaminePanel.RoundStyle.Right:
                    path.AddArc(
                        rect.Right - radius - radiusCorrection,
                        rect.Y,
                        radius,
                        radius,
                        270,
                        90);
                    path.AddArc(
                       rect.Right - radius - radiusCorrection,
                       rect.Bottom - radius - radiusCorrection,
                       radius,
                       radius,
                       0,
                       90);
                    path.AddLine(rect.X, rect.Bottom - radiusCorrection, rect.X, rect.Y);
                    break;
                case ExaminePanel.RoundStyle.Top:
                    path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
                    path.AddArc(
                        rect.Right - radius - radiusCorrection,
                        rect.Y,
                        radius,
                        radius,
                        270,
                        90);
                    path.AddLine(
                        rect.Right - radiusCorrection, rect.Bottom - radiusCorrection,
                        rect.X, rect.Bottom - radiusCorrection);
                    break;
                case ExaminePanel.RoundStyle.Bottom:
                    path.AddArc(
                        rect.Right - radius - radiusCorrection,
                        rect.Bottom - radius - radiusCorrection,
                        radius,
                        radius,
                        0,
                        90);
                    path.AddArc(
                        rect.X,
                        rect.Bottom - radius - radiusCorrection,
                        radius,
                        radius,
                        90,
                        90);
                    path.AddLine(rect.X, rect.Y, rect.Right - radiusCorrection, rect.Y);
                    break;
            }
            path.CloseFigure();

            return path;
        }
        #endregion
    }
}
